package com.og.dao;

import java.io.Serializable;
import java.util.*;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
import javax.persistence.TemporalType;

import org.apache.log4j.Logger;

import com.og.connection.EMF;

/** 
 * Class to perform entity CRUD with the database
 * 
 * @author Renaud DIANA
 */
public class CRUDEntityDAOImpl<T> implements CRUDEntityDAO<T>, Serializable {

	private static final long serialVersionUID = 1L;

	/**
     * Default constructor
     */
	public CRUDEntityDAOImpl(){
		super();
	}
	
	// Log4j	 
	private static final Logger	log	= Logger.getLogger(CRUDEntityDAOImpl.class);

	/**	 
	 * Class method to create a new entity to the database
	 * 
	 * @param t
	 *          Generic backing bean	 
	 * 
	 * @return 
	 * 			Validation flag
	 */
	@Override
	public boolean createEntity(T t) {
		
		boolean validationQuery = false;
		
		EntityManager em = EMF.get().createEntityManager();
		EntityTransaction tx = em.getTransaction();
	    try {
	        
	    	tx.begin();
	    	em.persist(t);
	    	em.flush();	    	
	    	tx.commit();
	    	em.refresh(t);
	    		    	
	    	// If query ok, validation flag = true
	    	validationQuery = true;
	    	
	    	log.debug("Creation " + t + " to database: Ok");
	    } finally {
	    	if(tx.isActive()) {
                tx.rollback();
                log.error("Creation " + t + " to database: Rollback!");
	    	 }
	        em.close();
	        log.debug("Close em : Ok");
	    }
	
		return validationQuery;
	}
	
	/**	 
	 * Class method to update an entity to the database
	 *
	 * @param t
	 *          Generic backing bean	 
	 * 
	 * @return 
	 * 			Validation flag
	 */
	@Override
	public boolean updateEntity(T t) {
		
		boolean validationQuery = false;

		EntityManager em = EMF.get().createEntityManager();
		EntityTransaction tx = em.getTransaction();
	    try {
	       
	    	tx.begin();
	    	em.merge(t);
	    	em.flush();	    	
	    	tx.commit();
	    	//em.refresh(gb);
	    		    	
	    	log.debug("Update " + t + " to database: Ok");
	    	
	    	// If query ok, validation flag = true
			validationQuery = true;
			
	    } finally {
	    	 if (tx.isActive()) {
                 tx.rollback(); 
                 log.debug("Update " + t + " to database: Rollback!");
	    	 }
	        em.close();
	        log.debug("Close em : Ok");
	    }		
						
		return validationQuery;
	}
	
	/**	 
	 * Class method to delete an entity from the database
	 * 
	 * @param t
	 *          Generic backing bean	 
	 * 
	 * @return 
	 * 			Validation flag
	 */
	@Override
	public boolean deleteEntity(T t) {
				
		boolean validationQuery = false;

		EntityManager em = EMF.get().createEntityManager();
		EntityTransaction tx = em.getTransaction();
	    try {
	        
	    	tx.begin();	    	
	    	em.remove(em.merge(t));
	    	em.flush();	    	
	    	tx.commit();
	    	//em.refresh(gb);
		   	    		    	
		    // If query ok, validation flag = true
	    	validationQuery = true;
	    	
	    	log.debug("Delete " + t + " from database: Ok");
	    } finally {
	    	 if(tx.isActive()) {
                 tx.rollback();
                 log.error("Delete " + t + " from database: Rollback!");
	    	 }
	    	em.close();
	        log.debug("Close em : Ok");
	    }
	    
		return validationQuery;	
	}
			
	/**	 
	 * Class method to find an entity from the database
	 * 	 
	 * @param t
	 *          Generic backing bean
	 * @param id
	 *          Backing bean's id to find
	 *                   
	 * @return 
	 * 			Generic backing bean
	 */	
	@Override
	public T findOneEntity(T t, Object id) {
		
		EntityManager em = EMF.get().createEntityManager();		
		Class<? extends Object> ec = t.getClass();
		try {
	        
	    	t = (T)em.find(ec, id);
	    	
	    	log.debug("Bean " + t + " find from database: Ok");
	    } finally {
	        em.close();
	        log.debug("Close em : Ok");
	    }
		
		return t;
	}
	
	/**	 
	 * Class method to find a result of entities based on a NamedQuery from the database
	 * @param <V>
	 * 	 
	 * @param namedQuery
	 *          The NamedQuery          
	 * @param t
	 *          Generic backing bean
	 * @param param
	 *          Query parameters
	 *
	 * @return 
	 * 			List of generic backing beans
	 */	
	@Override
	public <K, V> List<T> findByNamedQuery(String namedQuery, T t, Map<K, V> param) {
		
		List<T> listT = new ArrayList<T>();
		Class<? extends Object> ec = t.getClass();

		EntityManager em = EMF.get().createEntityManager();
	    try {
	    	
	    	Query query = em.createNamedQuery(namedQuery, ec);
	    	if(param != null) {
	    		
	    		Set<Map.Entry<K, V>> entries = param.entrySet();
	    		Iterator<Map.Entry<K, V>> itr = entries.iterator();
	    		while(itr.hasNext()){
	    			Map.Entry<K, V> entry = itr.next();
	    			if((boolean) entry.getKey().equals("date"))
	    				query.setParameter((String) entry.getKey(),(Calendar) entry.getValue(), TemporalType.DATE);
	    			else
	    				query.setParameter((String) entry.getKey(),entry.getValue());
	    			log.debug("entry.getValue: " + entry.getValue());
	    		}
	    	}
	    	listT = (List<T>) query.getResultList();
	    	log.debug("List " + t + " size: " + listT.size());
	       
	    	log.debug("Named query " + namedQuery + " find from database: Ok");
	    } finally {
	        em.close();
	        log.debug("Close em : Ok");
	    }
		
		return listT;
	}
	
	/**	 
	 * Class method to find a result of entities based on a customQuery from the database
	 * @param <V>
	 * 	 
	 * @param customQuery
	 *          The customQuery          
	 * @param t
	 *          Generic backing bean
	 *
	 * @return 
	 * 			List of generic backing beans
	 */
	@Override
	public <K, V> List<T> findByCustomQuery(String customQuery, T t, Map<K, V> param) {
		
		List<T> listT = new ArrayList<T>();
		Class<? extends Object> ec = t.getClass();

		EntityManager em = EMF.get().createEntityManager();
	    try {
	    	
	    	Query query = em.createQuery(customQuery, ec);
	    	if(param != null) {
	    		
	    		Set<Map.Entry<K, V>> entries = param.entrySet();
	    		Iterator<Map.Entry<K, V>> itr = entries.iterator();
	    		while(itr.hasNext()){
	    			Map.Entry<K, V> entry = itr.next();
	    			if((boolean) entry.getKey().equals("date"))
	    				query.setParameter((String) entry.getKey(),(Calendar) entry.getValue(), TemporalType.DATE);
	    			else
	    				query.setParameter((String) entry.getKey(),entry.getValue());
	    			log.debug("entry.getValue: " + entry.getValue());
	    		}
	    	}
	    	listT = (List<T>) query.getResultList();
	    	log.debug("List " + t + " size: " + listT.size());
	       
	    	log.debug("Custom query " + customQuery + " find from database: Ok");
	    } finally {
	        em.close();
	        log.debug("Close em : Ok");
	    }
		
		return listT;
	}
	
}